﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Transactions;
using System.Text;
using NUnit.Framework;
using SimpleFS.Core;
using SimpleFS.Services.Transactions;

namespace SimpleFS.Specs.Services.Transactions
{
    [TestFixture]
    public abstract class AbstractFileSystemObjectTest
    {
        protected abstract IFileSystemObject SourceObject
        {
            get;
        }

        protected abstract IFileSystemObject TargetObject
        {
            get;
        }

        protected abstract void EnsureObjectExists(IFileSystemObject objectName);

        protected abstract void EnsureObjectIsDeleted(IFileSystemObject objectName);

        [SetUp]
        public void SetUp()
        {
            this.EnsureObjectExists(this.SourceObject);
            this.EnsureObjectIsDeleted(this.TargetObject);
        }

        /// <summary>
        /// Copy is an atomic operation for directories, but not as the same for files;
        /// </summary>
        public virtual void TestCopy()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Copy(this.TargetObject);
                scope.Complete();
            }
            Assert.IsTrue(this.SourceObject.Exists());
            Assert.IsTrue(this.TargetObject.Exists());

            this.EnsureObjectIsDeleted(this.TargetObject);

            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Copy(this.TargetObject);
            }
            Assert.IsTrue(this.SourceObject.Exists());
            Assert.IsFalse(this.TargetObject.Exists());
        }

        /// <summary>
        /// Copy is an atomic operation for directories, but not as the same for files;
        /// </summary>
        public virtual void TestMove()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Move(this.TargetObject);
                scope.Complete();
            }
            Assert.IsFalse(this.SourceObject.Exists(), "Source object should not exist");
            Assert.IsTrue(this.TargetObject.Exists(), "Target object should exist");

            this.EnsureObjectExists(this.SourceObject);
            this.EnsureObjectIsDeleted(this.TargetObject);            

            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Move(this.TargetObject);
            }
            Assert.IsTrue(this.SourceObject.Exists());
            Assert.IsFalse(this.TargetObject.Exists());
        }

        public virtual void TestRename()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Rename(this.TargetObject.Name);
                scope.Complete();
            }
            Assert.IsFalse(this.SourceObject.Exists());
            Assert.IsTrue(this.TargetObject.Exists());

            this.EnsureObjectExists(this.SourceObject);
            this.EnsureObjectIsDeleted(this.TargetObject);

            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Rename(this.TargetObject.Name);
            }
            Assert.IsTrue(this.SourceObject.Exists());
            Assert.IsFalse(this.TargetObject.Exists());

            this.SourceObject.Close();
        }

        public virtual void TestDelete()
        {
            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Delete();
                scope.Complete();
            }
            Assert.IsFalse(this.SourceObject.Exists());

            this.EnsureObjectExists(this.SourceObject);

            using (TransactionScope scope = new TransactionScope())
            {
                this.SourceObject.Delete();
            }
            Assert.IsTrue(this.SourceObject.Exists());

            this.SourceObject.Close();
        }
    }
}
